/**
 *
 */
package dk.simonvogensen.itodo.shared.model;

import dk.simonvogensen.itodo.client.model.Project;
import dk.simonvogensen.itodo.client.model.ProjectId;
import dk.simonvogensen.itodo.client.model.TransportType;

import java.io.Serializable;
import java.util.*;

/**
 * @author sbv
 */
public class TodoItem implements Serializable {

    private static final String DAYMONTHYEAR_PATTERN = "\\d{1,2}/\\d{1,2}-\\d{2}";
    private static final String DAYMONTH_PATTERN = "\\d{1,2}/\\d{1,2}";
    private static final String WORKTIMEPERIOD_PATTERN = "\\d{0,2}t\\d{0,2}";
    private static final String TIMEFROMTOINTERVAL_PATTERN = "\\d{1,2}.\\d{2}\\s-\\s\\d{1,2}.\\d{2}";

    private String id;

    private String email;

    private int index;

    private String text;

    private TodoItem prevItem;

    boolean modified = false;

    private Date date;

    private TransportType transportType;

    private List<Project> projectList;

    private int worktimeInMinutes;

    private Config config;

    private List<TagItem> tagItemList;

    public TodoItem() {
    }

    public TodoItem(Config config, String id, String text, int index) {
        this(config, null, id, text, index);
    }

    public TodoItem(Config config, TodoItem prevItem, String id, String text, int index) {
        this.prevItem = prevItem;
        this.id = id;
        this.index = index;
        this.config = config;
        setText(text);
    }

    /**
     * This constructor is when object is used to send data to server.. this will cut the relations and stuff..
     * TODO pull persistentTodoItem out to client instead..
     */
    public TodoItem(String id, int index, String text) {
        this.id = id;
        this.index = index;
        this.text = text;
    }

    public String buildDefaultText(Date date) {
        String defaultText = getDateString(date) + " "+config.getDefaultTransportType()+"\n";
        defaultText += getStarredLines();
        return defaultText;
    }

    private String getStarredLines() {
        String result = "";
        if (prevItem != null) {
            String[] lines = prevItem.getText().split("\n");
            for (String line : lines) {
                if (line.contains("  -* ")) {
                    result += line;
                }
            }
        }
        return result;
    }

    public String getText() {
        return text;
    }

    public String getId() {
        return id;
    }

    public boolean isModified() {
        return modified;
    }

    public void setModified(boolean modified) {
        this.modified = modified;
    }

    public void setId(String id) {
        this.id = id;
    }

    public void setText(String text) {
        if (text != null) {
            this.text = text;
        } else {
            this.text = buildDefaultText(new Date());
        }
        updateFieldsFromText();
    }

    //TODO remove to gui util
    public String getShortHeaderText() {
        String headerText = null;
        if (date != null) {
            switch (date.getDay()) {
                case 0:
                    headerText = "sun ";
                    break;
                case 1:
                    headerText = "mon ";
                    break;
                case 2:
                    headerText = "tue ";
                    break;
                case 3:
                    headerText = "wed ";
                    break;
                case 4:
                    headerText = "thu ";
                    break;
                case 5:
                    headerText = "fri ";
                    break;
                case 6:
                    headerText = "sat ";
                    break;
            }
            headerText += getDateString(date);
        }
        return headerText;
    }

    private String getDateString(Date date) {
        int m = date.getMonth() + 1;
        int d = date.getDate();
        int y = date.getYear() - 100;
        return d + "/" + m + "-" + (y < 10 ? "0" + y : "" + y);
    }

    private String getTimeString(Date date) {
        int h = date.getHours();
        int m = date.getMinutes();
        return h + "." + (m < 10 ? "0" + m : "" + m);
    }

    public void updateFieldsFromText() {
        transportType = findTransportType();
        date = findDate();
        worktimeInMinutes = findOldWorktime();
        // only try find projects if nothing is found in first line
        if (worktimeInMinutes <= 0) {
            projectList = findProjectList();
        } else {
            projectList = findOldProjectList();
        }
        tagItemList = new ArrayList<TagItem>(TagItem.getAllTagItems(this));
    }

    private List<Project> findOldProjectList() {
        List<Project> projectList = new ArrayList<Project>();
        String[] lines = text.split("\n");
        if (lines.length > 1) {
            String[] projects = lines[1].split(",");
            for (int i = 0; i < projects.length; i++) {
                ProjectId projectId = ProjectId.contains(projects[i], config.getProjectIdList());
                if (projectId != null) {
                    projectList.add(new Project(projectId, 0));
                }
            }
        }
        return projectList;
    }

    private int findOldWorktime() {
        if (text == null) return 0;
        String[] lines = text.split("\n");
        int worktime = findWorktimeInString(lines[0]);
        if (transportType != null)
            worktime -= transportType.getTransportTime();
        return worktime > 0 ? worktime : 0;
    }

    private List<Project> findProjectList() {
        List<Project> projectList = new ArrayList<Project>();
        if (text == null) return projectList;
        String[] lines = text.split("\n");
        for (int i = 0; i < lines.length; i++) {
            ProjectId projectId = ProjectId.containsProject(lines[i], config.getProjectIdList());
            if (projectId != null) {
                projectList.add(new Project(projectId, findWorktimeInString(lines[i])));
            }
        }
        int transportTimeInMinutes = 0;
        if (transportType != null)
            transportTimeInMinutes = transportType.getTransportTime();
        for (int i = 0; i < projectList.size(); i++) {
            Project project = projectList.get(i);
            int transportTimeInMinutesPrProject = transportTimeInMinutes / projectList.size();
            project.substractTransportInWorktime(transportTimeInMinutesPrProject);
        }
        return projectList;
    }

    //TODO remove to gui util
    public String getHeaderText() {
        String headerText = getShortHeaderText();

        if (transportType != null) {
            headerText += " " + transportType.getName();
        }
        if (worktimeInMinutes != 0 && projectList.isEmpty()) { // old format
            headerText += " " + formatWorktime(worktimeInMinutes);
        }
        if (!projectList.isEmpty()) {
            headerText += " " + formatWorktime(getWorktimeInMinutes());
            headerText += " (";
            for (Iterator<Project> it = projectList.iterator(); it.hasNext();) {
                Project project = it.next();
                headerText += project.getName();
                if (project.getWorktimeInMinutes() > 0)
                    headerText += " " + formatWorktime(project.getWorktimeInMinutes());
                if (it.hasNext()) headerText += ", ";
            }
            headerText += ")";
        }
        if (headerText == null) {
            headerText = getFirstLine(text);
        }
        return headerText;
    }

    protected String formatWorktime(int worktimeInMinutes) {
        String headerText = "";
        int workHours = worktimeInMinutes / 60;
        if (workHours != 0) {
            headerText += workHours + "t";
        }

        int workMinutes = worktimeInMinutes % 60;
        if (workMinutes != 0) {
            if (workHours == 0) headerText += "t";
            headerText += (workMinutes < 10 ? "0" + workMinutes : "" + workMinutes);
        }
        return headerText;
    }

    private int findWorktimeInString(String s) {
        int totalMinutes = 0;
        int index1 = s.indexOf(" ") + 1;
        int index2 = s.indexOf(" ", index1) + 1;
        if (index2 > 0) {
            String[] periods = s.substring(index2).split("[,]");
            if (periods.length == 1 && periods[0].matches(WORKTIMEPERIOD_PATTERN)) {
                int hour = Integer.parseInt(periods[0].substring(0, periods[0].indexOf("t")).trim());
                int min = 0;
                if (!periods[0].endsWith("t")) {
                    min = Integer.parseInt(periods[0].substring(periods[0].indexOf("t") + 1).trim());
                }
                totalMinutes = hour * 60 + min;
            } else {
                for (int i = 0; i < periods.length; i++) {
                    if (periods[i].trim().matches("^" + TIMEFROMTOINTERVAL_PATTERN + "$")) {
                        String[] period = periods[i].split("[-]");
                        if (period.length == 2) {
                            int fromHour = Integer.parseInt(period[0].substring(0, period[0].indexOf(".")).trim());
                            int fromMin = Integer.parseInt(period[0].substring(period[0].indexOf(".") + 1).trim());
                            int toHour = Integer.parseInt(period[1].substring(0, period[1].indexOf(".")).trim());
                            int toMin = Integer.parseInt(period[1].substring(period[1].indexOf(".") + 1).trim());
                            totalMinutes += (toHour * 60 + toMin) - (fromHour * 60 + fromMin);
                        }
                    }
                }
            }
        }
        return totalMinutes;
    }

    private Date findDate() {
        String s = getFirstLine(text);
        if (s == null) return null;
        int index1 = s.indexOf(" ") + 1;
        String dateString;
        if (index1 == 0) {
            dateString = s;
        } else {
            dateString = s.substring(0, index1).trim();
        }
        if (dateString.matches("^" + DAYMONTHYEAR_PATTERN)) {
            int day = Integer.parseInt(dateString.substring(0, dateString.indexOf("/")));
            int month = Integer.parseInt(dateString.substring(dateString.indexOf("/") + 1, dateString.indexOf("-"))) - 1;
            int year = Integer.parseInt(dateString.substring(dateString.indexOf("-") + 1)) + 100;
            return new Date(year, month, day);

        } else if (dateString.matches("^" + DAYMONTH_PATTERN)) {
            // this is for backward compability - dates with no year - is 2008
            int day = Integer.parseInt(dateString.substring(0, dateString.indexOf("/")));
            int month = Integer.parseInt(dateString.substring(dateString.indexOf("/") + 1)) - 1;
            int year = 108;
            return new Date(year, month, day);
        }
        return null;
    }

    private TransportType findTransportType() {
        String s = getFirstLine(text);
        if (s == null) return null;
        int index1 = s.indexOf(" ") + 1;
        int index2 = s.indexOf(" ", index1);
        String transportType;
        if (index2 > 0) {
            transportType = s.substring(index1, index2).trim();
        } else {
            transportType = s.substring(index1).trim();
        }
        return TransportType.parseString(transportType, config.getTransportTypeList());
    }

    private String getFirstLine(String text) {
        if (text != null && text.indexOf('\n') != -1) {
            text = text.substring(0, text.indexOf('\n'));
        }
        return text;
    }

    public int getWorktimeInMonthUntilDate() {
        TodoItem item = this;
        int worktimeInMinutes = 0;
        do {
            worktimeInMinutes += item.getWorktimeInMinutes();
            item = item.getPrevItem();
            //TODO fix this - it should jump over not stop
        } while (item != null && this.getDate() != null && item.getDate() != null && this.getDate().getMonth() == item.getDate().getMonth());

        return worktimeInMinutes;
    }

    public int getWorktimeInMinutes() {
        int worktimeInMinutes = 0;
        for (int i = 0; i < projectList.size(); i++) {
            Project project = (Project) projectList.get(i);
            worktimeInMinutes += project.getWorktimeInMinutes();
        }
        // try get worktime from firstline
        if (worktimeInMinutes == 0) {
            worktimeInMinutes = this.worktimeInMinutes;
        }
        return worktimeInMinutes;
    }

    public int getMinimumWorktimeInMonthUntilDate() {
        if(date == null) return 0;
        Date anyDate = new Date();
        anyDate.setTime(date.getTime());
        int workDayCount = 0;
        int dayOfMonth = date.getDate();
        for (int i = 1; i <= dayOfMonth; i++) {
            anyDate.setDate(i);
            if (anyDate.getDay() != 0 && anyDate.getDay() != 6) { // not sat or sunday
                workDayCount++;
            }
        }
        return (workDayCount * 444); // 7.4 * 60
    }

    public Date getDate() {
        return date;
    }

    public TodoItem getPrevItem() {
        return prevItem;
    }

    public int getWorkHours() {
        return getWorktimeInMinutes() / 60;
    }

    public int getWorkMinutes() {
        return getWorktimeInMinutes() % 60;
    }

    public void addTime(ProjectId pi, Date date) {
        String[] lines = text.split("\n");
        for (int i = 0; i < lines.length; i++) {
            if (lines[i].matches("^-\\s" + pi.getName() + ".*$")) {
                int index = text.indexOf(lines[i]) + lines[i].length();
                if (lines[i].endsWith("\n")) index -= 1; // -1 -> before newline
                String preText = text.substring(0, index);
                String postText = text.substring(index);
                int lastComma = lines[i].lastIndexOf(", ");
                int lastSubst = lines[i].lastIndexOf("- ") - 1; // dont count first -
                boolean endsWithDigit = Character.isDigit(text.charAt(index - 1));
                String preTimeString = " ";
                if (endsWithDigit) {
                    if (lastComma < lastSubst) preTimeString = ", ";
                    else if (lastComma > lastSubst) preTimeString = " - ";
                    else preTimeString = " - ";
                }
                text = preText + preTimeString + getTimeString(date) + postText;
                updateFieldsFromText();
                return;
            }
        }
        text += "\n- " + pi.getName() + " " + getTimeString(date);
        updateFieldsFromText();
    }

    public TransportType getTransportType() {
        return transportType;
    }

    public List<Project> getProjectList() {
        return projectList;
    }

    public void changeTransportType(TransportType transportType) {
        String[] lines = text.split("\n");
        if (lines[0].matches("^" + DAYMONTHYEAR_PATTERN + "\\s.*$")) {
            int index = lines[0].indexOf(" ") + 1;
            int index2 = lines[0].indexOf(" ", index);
            if (index2 == -1) index2 = lines[0].length();
            text = text.substring(0, index) + transportType.getName() + text.substring(index2);
        }
        updateFieldsFromText();
    }

    public Project getProject(ProjectId projectId) {
        for (Project project : projectList) {
            if (project.getProjectId().equals(projectId)) {
                return project;
            }
        }
        return null;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getEmail() {
        return email;
    }

    public void setPrevItem(TodoItem prevItem) {
        this.prevItem = prevItem;
    }

    public int getIndex() {
        return index;
    }

    public void setIndex(int index) {
        this.index = index;
    }

    public void setConfig(Config config) {
        this.config = config;
    }

    public Config getConfig() {
        return config;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        TodoItem todoItem = (TodoItem) o;

        if (index != todoItem.index) return false;

        return true;
    }

    @Override
    public int hashCode() {
        return index;
    }

    public Collection<TagItem> getTagItemList() {
        return tagItemList;
    }
}
